/*
 * ShaderTypes.h
 *
 *  Created on: 25.09.2011
 *      Author: Blinov Kirill
 *      Copyright: LGPL
 */

#ifndef SHADERTYPES_H_
#define SHADERTYPES_H_

#include "../BasicTypes.h"
#include <boost/function.hpp>

class IShaderConfigurator
{
protected:
	IShaderConfigurator(){};
	virtual ~IShaderConfigurator(){};
public:
	virtual void SetParam( const GLint ParamName, const UINT Value ) = 0;
	virtual void SetParam( const GLint ParamName, const int Value ) = 0;
	virtual void SetParam( const GLint ParamName, const float Value ) = 0;
	virtual void SetParam( const GLint ParamName, const glm::vec3& Value ) = 0;
	virtual void SetParam( const GLint ParamName, const glm::vec4& Value ) = 0;
	virtual void SetParam( const GLint ParamName, const glm::mat4& Value ) = 0;
	virtual void SetParam( const GLint ParamName, const std::vector<glm::mat4>& Value ) = 0;
	virtual void SetParam( const GLint ParamName, const TEXTURE_DESCRIPTOR& Value ) = 0;
	virtual void SetParam( const GLint ParamName, const SHADER_INPUT_COMMON_VALUES Value ) = 0;
};

class IShaderParamContainer
{
protected:
	IShaderParamContainer(){};
	IShaderParamContainer( GLint Reference ): reference(Reference)
	{}
	virtual ~IShaderParamContainer(){};

	GLint reference;
public:
    virtual void SetParam( IShaderConfigurator* ) = 0;
};

template< class CallbackReturn >
class CShaderParamCallback: public IShaderParamContainer
{
private:
	CShaderParamCallback(){};

	boost::function <CallbackReturn()> getParamCallback;
public:
	CShaderParamCallback( const GLint Reference, boost::function <CallbackReturn()> GetParamCallback ) :
		getParamCallback( GetParamCallback ), IShaderParamContainer( Reference )
	{};
	virtual ~CShaderParamCallback(){};

	void SetParam( IShaderConfigurator* Configurator )
	{
		Configurator->SetParam( reference, getParamCallback() );
	}
};

template< class ConstantType >
class CShaderParamConstant: public IShaderParamContainer
{
private:
	CShaderParamConstant(){};

	ConstantType value;
public:
	CShaderParamConstant( const GLint Reference, const ConstantType &Value ) :
		IShaderParamContainer( Reference ), value(Value)
	{};
	virtual ~CShaderParamConstant(){};

	void SetParam( IShaderConfigurator* Configurator )
	{
		Configurator->SetParam( reference, value );
	}
};

//-----------------------------------------------------------------

class IShaderProgram
{
protected:
	IShaderProgram(){};
public:
	virtual ~IShaderProgram(){};

	virtual bool InitShader( const GLuint ProgrammName ) = 0;
	virtual void ActivateShader( const bool Activate ) = 0;
	virtual void SetParam( SHADER_COMMON_PARAMS &Params ) = 0;

	virtual GLint GetShaderParamReference( const std::string ParameterName ) = 0;
	virtual void  AddParamContainer( IShaderParamContainer* ) = 0;
};

class CShaderProgram: public IShaderProgram, IShaderConfigurator
{
private:
	void SetTextureToCurrentBlock( const GLint ParamName, const GLint TextureName, const GLenum TextureType );
protected:
	GLuint                              programName;
	std::vector<IShaderParamContainer*> containers;

	SHADER_COMMON_PARAMS               *tmpParamPointer;
	//Used for mapping texture blocks in shader every frame
	UINT                                textureBlockCounter;
public:
	CShaderProgram(): programName(0), textureBlockCounter(0) {};
	CShaderProgram( const GLuint ShaderName );

	virtual ~CShaderProgram();
	bool InitShader( const GLuint ProgramName );

	GLint GetShaderParamReference( const std::string ParameterName );
	void  AddParamContainer( IShaderParamContainer* Container );

	void SetParam( SHADER_COMMON_PARAMS &Params );
	void SetParam( const GLint ParamName, const UINT Value );
	void SetParam( const GLint ParamName, const int Value );
	void SetParam( const GLint ParamName, const float Value );
	void SetParam( const GLint ParamName, const glm::vec3& Value );
	void SetParam( const GLint ParamName, const glm::vec4& Value );
	void SetParam( const GLint ParamName, const glm::mat4& Value );
	void SetParam( const GLint ParamName, const std::vector<glm::mat4>& Value );
	void SetParam( const GLint ParamName, const TEXTURE_DESCRIPTOR& Value );
	void SetParam( const GLint ParamName, const SHADER_INPUT_COMMON_VALUES Value );
	void ActivateShader( const bool Activate ){ glUseProgram( Activate ? programName : 0 ); }; //Control shader usage
};

#endif /* SHADERTYPES_H_ */
